//+build jsonpbcpp

package jsonpbcpp

// #include <stdlib.h>
// #include "json_pb_module.h"
import "C"

import (
	"errors"
	"gitee.com/dennis-kk/service-box-go/internal/jsonpb"
	"reflect"
	"unsafe"
)

var (
	InitErr            = errors.New("init json pb cpp error ")
	InvalidCoderHandle = errors.New("no effective jsonpb cpp coder handle")
)

type (
	//JsonPbCpp jsonpb coder with cpp shared lib
	JsonPbCpp struct {
		//jbpHandle 编码器句柄
		jbpHandle C.int
		//options 配置信息
		options *jsonpb.Options
	}
)

func (jp *JsonPbCpp) Init(opts ...jsonpb.Option) error {
	//应用配置
	for _, opt := range opts {
		opt(jp.options)
	}

	if len(jp.options.CfgPath) == 0 {
		return jsonpb.InvalidConfigPath
	}

	return jp.doInit()
}

//JsonStrToProtoBuffer 序列化json 格式 参数到pb 字节流
func (jp *JsonPbCpp) JsonStrToProtoBuffer(service string, method string, args string) ([]byte, error) {
	if jp.jbpHandle <= 0 {
		return nil, InvalidCoderHandle
	}

	//方法名，服务名拷贝为c++ 字符串
	cService := C.CString(service)
	cMethod := C.CString(method)

	// 销毁函数一一对应
	defer func() {
		C.free(unsafe.Pointer(cService))
		C.free(unsafe.Pointer(cMethod))
	}()

	// hack 做法，因为参数可能较大，为了性能，这里直接进行内存强转
	rStrHeader := (*reflect.StringHeader)(unsafe.Pointer(&args))
	// 不要在这里添加代码，cgo 并不保证c函数调用前goroutine 的内存是不会移动的
	pbBuffer := C.json_pb_to_pb_bytes(jp.jbpHandle, cService, cMethod, (*C.char)(unsafe.Pointer(rStrHeader.Data)))
	if pbBuffer == nil {
		//发生错误, 获取错误，并返回
		errStr := C.json_pb_get_error_string(jp.jbpHandle)
		goErrStr := C.GoString(errStr)
		return nil, errors.New(goErrStr)
	}

	// 将c++ 内存拷贝到go中
	pbLen := C.json_pb_get_pb_bytes_length(jp.jbpHandle)
	msgPack := C.GoBytes(unsafe.Pointer(pbBuffer), pbLen)
	return msgPack, nil
}

//ProtoBufferToJson 将pb 二进制转化为json
func (jp *JsonPbCpp) ProtoBufferToJson(service string, method string, pbBuffer []byte) (string, error) {
	if jp.jbpHandle <= 0 {
		return "", InvalidCoderHandle
	}

	//方法名，服务名拷贝为c++ 字符串
	cService := C.CString(service)
	cMethod := C.CString(method)

	// 销毁函数一一对应
	defer func() {
		C.free(unsafe.Pointer(cService))
		C.free(unsafe.Pointer(cMethod))
	}()

	// hack 不拷贝，直接将go的byte 传入c++中
	sliceHdr := (*reflect.SliceHeader)(unsafe.Pointer(&pbBuffer))

	cJsonRet := C.json_pb_to_json_string(jp.jbpHandle, cService, cMethod, (*C.char)(unsafe.Pointer(sliceHdr.Data)), C.int(sliceHdr.Len))
	if cJsonRet == nil {
		//发生错误, 获取错误，并返回
		errStr := C.json_pb_get_error_string(jp.jbpHandle)
		goErrStr := C.GoString(errStr)
		return "", errors.New(goErrStr)
	}
	return C.GoString(cJsonRet), nil
}

func (jp *JsonPbCpp) UnInit() error {
	res := C.json_pb_deinit(jp.jbpHandle)
	if res != 0 {
		//发生错误, 获取错误，并返回
		errStr := C.json_pb_get_error_string(jp.jbpHandle)
		goErrStr := C.GoString(errStr)
		return errors.New(goErrStr)
	}

	jp.jbpHandle = 0
	return nil
}

func (jp *JsonPbCpp) Restart() error {
	// 卸载原始模块
	if err := jp.UnInit(); err != nil {
		return err
	}
	return jp.doInit()
}

func (jp *JsonPbCpp) doInit() error {
	// 初始化CGo 模块
	jp.jbpHandle = C.json_pb_init()
	if jp.jbpHandle <= 0 {
		return InitErr
	}

	// 设置json与 proto 文件路径
	cCfgPath := C.CString(jp.options.CfgPath)
	// 配套销毁 代码
	defer C.free(unsafe.Pointer(cCfgPath))

	res := C.json_pb_set_directory(jp.jbpHandle, cCfgPath)
	if res != 1 {
		// 这块内存是c管理的，并非在go中调用malloc 申请，不需要释放
		errStr := C.json_pb_get_error_string(jp.jbpHandle)
		return errors.New(C.GoString(errStr))
	}

	return nil
}
